Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

对树状数组的理解加深了!

转化题意

动态维护一个单调不降和一个单调不增序列,每次修改后输出两序列取最小值后的最大值和其最大位置。

思路

首先,阅读原题,知道最后答案一定是某个战士的温度,所以我们将温度离散化。

再次阅读,发现冰系是一个(以温度为自变量的)单调不降序列,每次修改一个战士就是修改一段后缀。火系相反,修改前缀。

深度阅读,发现题目其实就是求两个序列的交点。于是转化成上述题意。

考虑二分答案求出两点的交,即温度最大的冰系能量和不大于火系能量和的位置。如果用树状数组动态维护每次查询的话,时间复杂度是 \(O(log^2n)\) ,无法通过。

实际上,树状数组也是可以二分的。回忆树状数组的树形结构,一个点 \(P+2^i\) 实际上存储了 \(P+1\)\(P+2^i\) 的所有信息,所以我们在树状数组上二分,实际上就是像倍增一样,从大到小依次枚举这个点加上 \(2^i\) 的祖先,能跳则跳。

于是我们就可以通过这题了。注意我这里存储火系前缀和的方式是记录一个全局和再在树状数组上减去。

最后答案乘二。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<cmath>
using namespace std;
inline int read(){
int x=0,w=0;char c=getchar();
while(!isdigit(c)) w|=c=='-',c=getchar();
while(isdigit(c)) x=x*10+(c^48),c=getchar();
return w?-x:x;
}
namespace star
{
const int maxn=4e6+10;
int n,b[maxn],cnt,sumfire;
struct BIT{
int c[maxn];
inline void update(int x,int k){for(;x<=cnt;x+=x&-x)c[x]+=k;}
}S1,S2;
inline int query(int x){
if(x<1 or x>cnt) return 0;
int ans1=0,ans2=sumfire;
for(;x;x-=x&-x) ans1+=S1.c[x],ans2+=S2.c[x];
return min(ans1,ans2);
}
struct query{
bool type;
int t,v,op;
}e[maxn];
inline void work(){
n=read();
for(int i=1;i<=n;i++)
if((e[i].op=read())==1)
e[i].type=read(),e[i].t=b[++cnt]=read(),e[i].v=read();
else e[i]=e[read()],e[i].op=2,e[i].v=-e[i].v;
sort(b+1,b+1+cnt);
cnt=unique(b+1,b+1+cnt)-b-1;
for(int i=1;i<=n;i++) e[i].t=lower_bound(b+1,b+1+cnt,e[i].t)-b;
for(int i=1;i<=n;i++){
if(e[i].type==0) S1.update(e[i].t,e[i].v);
else S2.update(e[i].t+1,-e[i].v),sumfire+=e[i].v;
int x1=0,sum=-sumfire;
for(int j=21;~j;j--){
int to=x1|(1<<j),zp;
if(to<=cnt and (zp=sum+S1.c[to]-S2.c[to])<=0) sum=zp,x1=to;
}
int ans1=query(x1),x2=x1+1,ans2=query(x2);
if(ans1<=0 and ans2<=0)puts("Peace");
else if(ans1>ans2) printf("%d %d\n",b[x1],ans1<<1);
else {
int p=0,sum=sumfire;
for(int j=21;~j;j--){
int to=p|(1<<j),zp;
if(to<=cnt and (zp=sum+S2.c[to])>=ans2) sum=zp,p=to;
}//因为现在的情况一定是火系战士能量低所以光用火系的找答案就行了
printf("%d %d\n",b[p],sum<<1);
}
}
}
}
signed main(){
star::work();
return 0;
}

其他

  • 三分是不正确的因为这玩意的峰不知道在哪而且会有平台。
  • 如果用其他方式存火系的信息请务必考虑是否算进了当前点的值。
  • 听说这题数据特水每次暴力移动上次答案的指针都能过
  • 输出太大在洛谷上OLE了,但LOJ上可以提交,希望管理员修复一下(已修复)

给小狼留言